package Transaction;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;


public class Concurrency {
	static HashMap < String, ReentrantReadWriteLock > map = new HashMap < String, ReentrantReadWriteLock> ();
	static HashMap < Integer, HashSet < ReentrantReadWriteLock > > allReadLocks = new HashMap < Integer, HashSet < ReentrantReadWriteLock > >(); 
	static HashMap < Integer, HashSet < ReentrantReadWriteLock > > allWriteLocks = new HashMap < Integer, HashSet < ReentrantReadWriteLock > >();
	static HashMap < Integer, Thread > readHolder = new HashMap < Integer, Thread > () ; 
	static long TIMEOUT = 50;
	static long TRYS = 10;
	static Object workAsLock = new Object() ;
	
	public static synchronized void initTransaction ( int txnum ){
		HashSet < ReentrantReadWriteLock >  set = new HashSet < ReentrantReadWriteLock >  () ;
		allReadLocks.put ( txnum, set ) ; 
		set = new HashSet < ReentrantReadWriteLock >  () ;
		allWriteLocks.put ( txnum, set ) ; 
	}
	
	
	public static boolean SLock ( int txnum, String table ) {
		HashSet < ReentrantReadWriteLock > readLocks = allReadLocks.get( txnum ) ;
		HashSet < ReentrantReadWriteLock > writeLocks = allWriteLocks.get( txnum ) ;
		ReentrantReadWriteLock lock = null ;
		if ( readLocks == null || writeLocks == null ){
			System.out.println ( txnum ) ; 
			return false ; 
		}
		try {
			if ( ( lock = map.get(table.toLowerCase() )) == null ) {
				lock = new ReentrantReadWriteLock() ;
				map.put( table.toLowerCase() , lock ) ;
			}
			if  ( readLocks.contains( lock ) || writeLocks.contains( lock ) ) return true ;
			synchronized ( workAsLock ) {
				if ( ! lock.readLock().tryLock(TIMEOUT, TimeUnit.MILLISECONDS )) return false ;
				else {
					readLocks.add( lock ) ; 
					return true; 
				}
			}
		}catch ( Exception e ) {
			e.printStackTrace() ; 
			return false ; 
		}
	}
	
	public static boolean XLock ( int txnum,  String table ) {
		HashSet < ReentrantReadWriteLock > readLocks = allReadLocks.get( txnum ) ;
		HashSet < ReentrantReadWriteLock > writeLocks = allWriteLocks.get( txnum ) ;
		ReentrantReadWriteLock lock = null ;
		int lockCount = 0 ; 
		
		try { 
			if ( ( lock = map.get(table.toLowerCase() )) == null ) {
				lock = new ReentrantReadWriteLock() ;
				map.put( table.toLowerCase(), lock ) ;
			}
			
			if ( lock.isWriteLockedByCurrentThread() ) return true ;
			if ( readLocks.contains( lock )) lockCount = 1 ; 

			for ( int i = 0 ; i < TRYS ; i ++ ) {
				synchronized ( workAsLock ) {
					if ( lock.getReadLockCount() == lockCount )  {
						if ( lockCount == 1 ) {
							lock.readLock().unlock() ;
							readLocks.remove( lock ) ;						
						}
						if ( lock.writeLock().tryLock()){
							writeLocks.add( lock ) ;
							return true; 
						} else return false; 
					}
				}
				if ( i < TRYS - 1 ) Thread.sleep( TIMEOUT / ( TRYS - 1 )) ;
			}
			return false ;
		} catch ( Exception e ) {
			e.printStackTrace() ; 
			return false;  
		}
	}
	
	public static synchronized void releaseAll ( int txnum ) {
		HashSet < ReentrantReadWriteLock > readLocks = allReadLocks.get( txnum ) ;
		HashSet < ReentrantReadWriteLock > writeLocks = allWriteLocks.get( txnum ) ;
		
		for ( ReentrantReadWriteLock lock : readLocks ) lock.readLock().unlock() ;
		for ( ReentrantReadWriteLock lock : writeLocks ) lock.writeLock().unlock() ;
		allReadLocks.remove( txnum ); 
		allWriteLocks.remove( txnum ); 
	}
}
